package com.hzy.utils;


import io.vertx.core.Future;
import io.vertx.core.Vertx;
import io.vertx.core.json.JsonObject;
import io.vertx.mysqlclient.MySQLConnectOptions;
import io.vertx.mysqlclient.MySQLPool;
import io.vertx.sqlclient.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.function.Function;

/**
 * @author huangzhenyu
 * @date 2024/9/10 23:03
 */
public class DbUtils {

    private static final Logger log = LoggerFactory.getLogger(DbUtils.class);

    protected static Pool client = null;


    public static void initializer(Vertx vertx) {
        registerDbPool(vertx);
        QueryUtil.registerConverters();
    }

    private static void registerDbPool(Vertx vertx) {
        JsonObject config = (JsonObject) vertx.sharedData().getLocalMap("config").get("appConfig");
        JsonObject dbConfig = config.getJsonObject("database");

        String host = dbConfig.getString("host", "127.0.0.1");
        Integer port = dbConfig.getInteger("port", 3306);
        String database = dbConfig.getString("database", "test");
        String user = dbConfig.getString("username", "root");
        String password = dbConfig.getString("password", "");
        Integer pipeliningLimit = dbConfig.getInteger("pipeliningLimit", 16);

        log.debug("[mysql] Connecting to database with settings: host={}, port={}, database={}, username={}, pipeliningLimit={}", host, port, database, user, pipeliningLimit);

        MySQLConnectOptions connectOptions = new MySQLConnectOptions()
                .setPort(port)
                .setHost(host)
                .setDatabase(database)
                .setUser(user)
                .setPassword(password)
                .setPipeliningLimit(pipeliningLimit);

        PoolOptions poolOptions = new PoolOptions()
                .setMaxSize(dbConfig.getInteger("maxSize", 8));

        client = MySQLPool.pool(vertx, connectOptions, poolOptions);
    }


    public static Pool getClient() {
        return client;
    }

    public static Future<SqlConnection> getConnection() {
        return client.getConnection();
    }


    /**
     * 查询数据并将结果映射为实体列表
     *
     * @param sql 查询 SQL 语句
     * @return 包含实体列表的 Future
     */
    public static Future<RowSet<Row>> query(String sql) {
        return QueryUtil.query(sql);
    }

    /**
     * 查询数据并将结果映射为实体列表
     *
     * @param sql 查询 SQL 语句
     * @return 包含实体列表的 Future
     */
    public static Future<RowSet<Row>> query(String sql, Tuple params) {
        return QueryUtil.query(sql, params);
    }

    /**
     * 查询数据并将结果映射为实体列表
     *
     * @param sql   查询 SQL 语句
     * @param clazz 实体类的 Class 对象
     * @param <T>   实体类类型
     * @return 包含实体列表的 Future
     */
    public static <T> Future<List<T>> query(String sql, Class<T> clazz) {
        return QueryUtil.query(sql, Tuple.tuple(), clazz);
    }

    /**
     * 查询数据并将结果映射为实体列表
     *
     * @param sql    查询 SQL 语句
     * @param params 查询参数
     * @param clazz  实体类的 Class 对象
     * @param <T>    实体类类型
     * @return 包含实体列表的 Future
     */
    public static <T> Future<List<T>> query(String sql, List<?> params, Class<T> clazz) {
        return QueryUtil.query(sql, Tuple.from(params), clazz);
    }

    /**
     * 查询数据并将结果映射为实体列表
     *
     * @param sql    查询 SQL 语句
     * @param params 查询参数
     * @param clazz  实体类的 Class 对象
     * @param <T>    实体类类型
     * @return 包含实体列表的 Future
     */
    public static <T> Future<List<T>> query(String sql, Tuple params, Class<T> clazz) {
        return QueryUtil.query(sql, params, clazz);
    }

    public static Future<RowSet<Row>> execute(String sql) {
        return client.query(sql).execute();
    }

    /**
     * 插入数据
     *
     * @param sql 插入 SQL 语句
     * @return 包含插入结果的 Future
     */
    public static Future<RowSet<Row>> insert(String sql) {
        return client.query(sql).execute();
    }

    /**
     * 插入数据
     *
     * @param sql    插入 SQL 语句
     * @param params 插入参数
     * @return 包含插入结果的 Future
     */
    public static Future<RowSet<Row>> insert(String sql, Tuple params) {
        return client.preparedQuery(sql).execute(params);
    }


    /**
     * 更新数据
     *
     * @param sql 更新 SQL 语句
     * @return 包含更新结果的 Future
     */
    public static Future<RowSet<Row>> update(String sql) {
        return client.preparedQuery(sql).execute();
    }

    /**
     * 更新数据
     *
     * @param sql    更新 SQL 语句
     * @param params 更新参数
     * @return 包含更新结果的 Future
     */
    public static Future<RowSet<Row>> update(String sql, Tuple params) {
        return client.preparedQuery(sql).execute(params);
    }

    /**
     * 删除数据
     *
     * @param sql 删除 SQL 语句
     * @return 包含删除结果的 Future
     */
    public static Future<RowSet<Row>> delete(String sql) {
        return client.preparedQuery(sql).execute();
    }

    /**
     * 删除数据
     *
     * @param sql    删除 SQL 语句
     * @param params 删除参数
     * @return 包含删除结果的 Future
     */
    public static Future<RowSet<Row>> delete(String sql, Tuple params) {
        return client.preparedQuery(sql).execute(params);
    }

    /**
     * 在一个事务中执行一系列数据库操作。
     * 此方法会自动处理连接的获取、事务的开启、提交或回滚，以及连接的最终关闭。
     *
     * @param actions 一个函数，它接收一个启用了事务的 SqlConnection，并返回一个 Future 来表示其操作的完成状态。
     * @param <T>     事务操作最终返回结果的类型
     * @return 一个 Future，如果事务成功提交，它将成功完成并携带 actions 的返回结果；如果事务回滚，它将失败。
     */
    public static <T> Future<T> withTransaction(Function<SqlConnection, Future<T>> actions) {
        return client.getConnection()
                .compose(connection -> connection
                        .begin()
                        .compose(tx ->
                                actions.apply(connection)
                                        .compose(result -> tx.commit().map(result))
                                        .recover(err -> tx.rollback().compose(v -> Future.failedFuture(err))))
                        .eventually(() -> connection.close()));
    }

}
